hypercall and vcpu-related schedop commands.
Signed-off-by: Keir Fraser <keir@xensource.com>
#include <asm/irq.h>
#include <asm/desc.h>
#include <asm-xen/xen-public/physdev.h>
+#include <asm-xen/xen-public/vcpu.h>
#ifdef CONFIG_MATH_EMULATION
#include <asm/math_emu.h>
#endif
don't printk. */
__get_cpu_var(cpu_state) = CPU_DEAD;
/* Tell hypervisor to take vcpu down. */
- HYPERVISOR_vcpu_down(cpu);
+ HYPERVISOR_vcpu_op(VCPUOP_down, cpu, NULL);
#endif
play_dead();
local_irq_enable();
#include <smpboot_hooks.h>
#include <asm-xen/evtchn.h>
+#include <asm-xen/xen-public/vcpu.h>
/* Set if we find a B stepping CPU */
static int __initdata smp_b_stepping;
ctxt.ctrlreg[3] = virt_to_mfn(swapper_pg_dir) << PAGE_SHIFT;
- boot_error = HYPERVISOR_boot_vcpu(cpu, &ctxt);
+ boot_error = HYPERVISOR_vcpu_op(VCPUOP_create, cpu, &ctxt);
if (boot_error)
printk("boot error: %ld\n", boot_error);
if (!boot_error) {
+ HYPERVISOR_vcpu_op(VCPUOP_up, cpu, NULL);
+
/*
* allow APs to start initializing.
*/
#ifdef CONFIG_HOTPLUG_CPU
#ifdef CONFIG_XEN
/* Tell hypervisor to bring vcpu up. */
- HYPERVISOR_vcpu_up(cpu);
+ HYPERVISOR_vcpu_op(VCPUOP_up, cpu, NULL);
#endif
/* Already up, and in cpu_quiescent now? */
if (cpu_isset(cpu, smp_commenced_mask)) {
ctxt.ctrlreg[3] = virt_to_mfn(swapper_pg_dir) << PAGE_SHIFT;
- (void)HYPERVISOR_boot_vcpu(vcpu, &ctxt);
+ (void)HYPERVISOR_vcpu_op(VCPUOP_create, vcpu, &ctxt);
+ (void)HYPERVISOR_vcpu_op(VCPUOP_up, vcpu, NULL);
}
#include <asm/nmi.h>
#ifdef CONFIG_XEN
#include <asm/arch_hooks.h>
-
#include <asm-xen/evtchn.h>
+#include <asm-xen/xen-public/vcpu.h>
#endif
/* Change for real CPU hotplug. Note other files need to be fixed
ctxt.ctrlreg[3] = virt_to_mfn(init_level4_pgt) << PAGE_SHIFT;
- boot_error = HYPERVISOR_boot_vcpu(cpu, &ctxt);
+ boot_error = HYPERVISOR_vcpu_op(VCPUOP_create, cpu, &ctxt);
if (boot_error)
printk("boot error: %ld\n", boot_error);
if (!boot_error) {
+ HYPERVISOR_vcpu_op(VCPUOP_up, cpu, NULL);
+
/*
* allow APs to start initializing.
*/
}
static inline int
-HYPERVISOR_boot_vcpu(
- unsigned long vcpu, vcpu_guest_context_t *ctxt)
+HYPERVISOR_vcpu_op(
+ int cmd, int vcpuid, void *extra_args)
{
- return _hypercall2(int, boot_vcpu, vcpu, ctxt);
-}
-
-static inline int
-HYPERVISOR_vcpu_up(
- int vcpu)
-{
- return _hypercall2(int, sched_op, SCHEDOP_vcpu_up |
- (vcpu << SCHEDOP_vcpushift), 0);
-}
-
-static inline int
-HYPERVISOR_vcpu_pickle(
- int vcpu, vcpu_guest_context_t *ctxt)
-{
- return _hypercall2(int, sched_op, SCHEDOP_vcpu_pickle |
- (vcpu << SCHEDOP_vcpushift), ctxt);
+ return _hypercall3(int, vcpu_op, cmd, vcpuid, extra_args);
}
static inline int
return ret;
}
-static inline int
-HYPERVISOR_vcpu_down(
- int vcpu)
-{
- int ret;
- unsigned long ign1;
- /* Yes, I really do want to clobber edx here: when we resume a
- vcpu after unpickling a multi-processor domain, it returns
- here, but clobbers all of the call clobbered registers. */
- __asm__ __volatile__ (
- TRAP_INSTR
- : "=a" (ret), "=b" (ign1)
- : "0" (__HYPERVISOR_sched_op),
- "1" (SCHEDOP_vcpu_down | (vcpu << SCHEDOP_vcpushift))
- : "memory", "ecx", "edx" );
- return ret;
-}
-
#endif /* __HYPERCALL_H__ */
/*
return 1;
}
-static inline int
-HYPERVISOR_boot_vcpu(
- unsigned long vcpu, vcpu_guest_context_t *ctxt)
-{
-#if 0
- int ret;
- unsigned long ign1, ign2;
-
- __asm__ __volatile__ (
- TRAP_INSTR
- : "=a" (ret), "=b" (ign1), "=c" (ign2)
- : "0" (__HYPERVISOR_boot_vcpu), "1" (vcpu), "2" (ctxt)
- : "memory");
-
- return ret;
-#endif
- return 1;
-}
#endif
#endif /* __HYPERCALL_H__ */
}
static inline int
-HYPERVISOR_boot_vcpu(
- unsigned long vcpu, vcpu_guest_context_t *ctxt)
+HYPERVISOR_vcpu_op(
+ int cmd, int vcpuid, void *extra_args)
{
- return _hypercall2(int, boot_vcpu, vcpu, ctxt);
-}
-
-static inline int
-HYPERVISOR_vcpu_up(
- int vcpu)
-{
- return _hypercall2(int, sched_op, SCHEDOP_vcpu_up |
- (vcpu << SCHEDOP_vcpushift), 0);
-}
-
-static inline int
-HYPERVISOR_vcpu_pickle(
- int vcpu, vcpu_guest_context_t *ctxt)
-{
- return _hypercall2(int, sched_op, SCHEDOP_vcpu_pickle |
- (vcpu << SCHEDOP_vcpushift), ctxt);
+ return _hypercall3(int, vcpu_op, cmd, vcpuid, extra_args);
}
static inline int
.long do_vm_assist
.long do_update_va_mapping_otherdomain
.long do_switch_vm86
- .long do_boot_vcpu
+ .long do_vcpu_op
.long do_ni_hypercall /* 25 */
.long do_mmuext_op
.long do_acm_op /* 27 */
.byte 2 /* do_vm_assist */
.byte 5 /* do_update_va_mapping_otherdomain */
.byte 0 /* do_switch_vm86 */
- .byte 2 /* do_boot_vcpu */
+ .byte 3 /* do_vcpu_op */
.byte 0 /* do_ni_hypercall */ /* 25 */
.byte 4 /* do_mmuext_op */
.byte 1 /* do_acm_op */
.quad do_vm_assist
.quad do_update_va_mapping_otherdomain
.quad do_switch_to_user
- .quad do_boot_vcpu
+ .quad do_vcpu_op
.quad do_set_segment_base /* 25 */
.quad do_mmuext_op
.quad do_acm_op
.byte 2 /* do_vm_assist */
.byte 4 /* do_update_va_mapping_otherdomain */
.byte 0 /* do_switch_to_user */
- .byte 2 /* do_boot_vcpu */
+ .byte 3 /* do_vcpu_op */
.byte 2 /* do_set_segment_base */ /* 25 */
.byte 4 /* do_mmuext_op */
.byte 1 /* do_acm_op */
#include <xen/domain_page.h>
#include <asm/debugger.h>
#include <public/dom0_ops.h>
+#include <public/vcpu.h>
/* Both these structures are protected by the domlist_lock. */
rwlock_t domlist_lock = RW_LOCK_UNLOCKED;
return rc;
}
-/*
- * final_setup_guest is used for final setup and launching of domains other
- * than domain 0. ie. the domains that are being built by the userspace dom0
- * domain builder.
- */
-long do_boot_vcpu(unsigned long vcpu, struct vcpu_guest_context *ctxt)
+int boot_vcpu(struct domain *d, int vcpuid, struct vcpu_guest_context *ctxt)
{
- struct domain *d = current->domain;
struct vcpu *v;
- int rc = 0;
- struct vcpu_guest_context *c;
+ int rc;
- if ( (vcpu >= MAX_VIRT_CPUS) || (d->vcpu[vcpu] != NULL) )
- return -EINVAL;
+ ASSERT(d->vcpu[vcpuid] == NULL);
- if ( alloc_vcpu_struct(d, vcpu) == NULL )
+ if ( alloc_vcpu_struct(d, vcpuid) == NULL )
return -ENOMEM;
- if ( (c = xmalloc(struct vcpu_guest_context)) == NULL )
- {
- rc = -ENOMEM;
- goto out;
- }
-
- if ( copy_from_user(c, ctxt, sizeof(*c)) )
- {
- rc = -EFAULT;
- goto out;
- }
-
- v = d->vcpu[vcpu];
+ v = d->vcpu[vcpuid];
atomic_set(&v->pausecnt, 0);
v->cpumap = CPUMAP_RUNANYWHERE;
arch_do_boot_vcpu(v);
- if ( (rc = arch_set_info_guest(v, c)) != 0 )
+ if ( (rc = arch_set_info_guest(v, ctxt)) != 0 )
goto out;
sched_add_domain(v);
- /* domain_unpause_by_systemcontroller */
- if ( test_and_clear_bit(_VCPUF_ctrl_pause, &v->vcpu_flags) )
- vcpu_wake(v);
+ set_bit(_VCPUF_down, &v->vcpu_flags);
+ clear_bit(_VCPUF_ctrl_pause, &v->vcpu_flags);
- xfree(c);
return 0;
out:
- xfree(c);
- arch_free_vcpu_struct(d->vcpu[vcpu]);
- d->vcpu[vcpu] = NULL;
+ arch_free_vcpu_struct(d->vcpu[vcpuid]);
+ d->vcpu[vcpuid] = NULL;
+ return rc;
+}
+
+long do_vcpu_op(int cmd, int vcpuid, void *arg)
+{
+ struct domain *d = current->domain;
+ struct vcpu *v;
+ struct vcpu_guest_context *ctxt;
+ long rc = 0;
+
+ if ( (vcpuid < 0) || (vcpuid >= MAX_VIRT_CPUS) )
+ return -EINVAL;
+
+ if ( ((v = d->vcpu[vcpuid]) == NULL) && (cmd != VCPUOP_create) )
+ return -ENOENT;
+
+ switch ( cmd )
+ {
+ case VCPUOP_create:
+ if ( (ctxt = xmalloc(struct vcpu_guest_context)) == NULL )
+ {
+ rc = -ENOMEM;
+ break;
+ }
+
+ if ( copy_from_user(ctxt, arg, sizeof(*ctxt)) )
+ {
+ xfree(ctxt);
+ rc = -EFAULT;
+ break;
+ }
+
+ LOCK_BIGLOCK(d);
+ rc = (d->vcpu[vcpuid] == NULL) ? boot_vcpu(d, vcpuid, ctxt) : -EEXIST;
+ UNLOCK_BIGLOCK(d);
+
+ xfree(ctxt);
+ break;
+
+ case VCPUOP_up:
+ if ( test_and_clear_bit(_VCPUF_down, &v->vcpu_flags) )
+ vcpu_wake(v);
+ break;
+
+ case VCPUOP_down:
+ if ( !test_and_set_bit(_VCPUF_down, &v->vcpu_flags) )
+ vcpu_sleep_nosync(v);
+ break;
+
+ case VCPUOP_is_up:
+ rc = !test_bit(_VCPUF_down, &v->vcpu_flags);
+ break;
+ }
+
return rc;
}
return 0;
}
-/* Mark target vcpu as non-runnable so it is not scheduled */
-static long do_vcpu_down(int vcpu)
-{
- struct vcpu *target;
-
- if ( vcpu > MAX_VIRT_CPUS )
- return -EINVAL;
-
- target = current->domain->vcpu[vcpu];
- if ( target == NULL )
- return -ESRCH;
- set_bit(_VCPUF_down, &target->vcpu_flags);
-
- return 0;
-}
-
-/* Mark target vcpu as runnable and wake it */
-static long do_vcpu_up(int vcpu)
-{
- struct vcpu *target;
-
- if (vcpu > MAX_VIRT_CPUS)
- return -EINVAL;
-
- target = current->domain->vcpu[vcpu];
- if ( target == NULL )
- return -ESRCH;
- clear_bit(_VCPUF_down, &target->vcpu_flags);
- /* wake vcpu */
- vcpu_wake(target);
-
- return 0;
-}
-
-static long do_vcpu_pickle(int vcpu, unsigned long arg)
-{
- struct vcpu *v;
- vcpu_guest_context_t *c;
- int ret = 0;
-
- if (vcpu >= MAX_VIRT_CPUS)
- return -EINVAL;
- v = current->domain->vcpu[vcpu];
- if (!v)
- return -ESRCH;
- /* Don't pickle vcpus which are currently running */
- if (!test_bit(_VCPUF_down, &v->vcpu_flags)) {
- return -EBUSY;
- }
- c = xmalloc(vcpu_guest_context_t);
- if (!c)
- return -ENOMEM;
- arch_getdomaininfo_ctxt(v, c);
- if (copy_to_user((vcpu_guest_context_t *)arg,
- (const vcpu_guest_context_t *)c, sizeof(*c)))
- ret = -EFAULT;
- xfree(c);
- return ret;
-}
-
-/*
- * Demultiplex scheduler-related hypercalls.
- */
long do_sched_op(unsigned long op, unsigned long arg)
{
long ret = 0;
domain_shutdown((u8)(op >> SCHEDOP_reasonshift));
break;
}
- case SCHEDOP_vcpu_down:
- {
- ret = do_vcpu_down((int)(op >> SCHEDOP_vcpushift));
- break;
- }
- case SCHEDOP_vcpu_up:
- {
- ret = do_vcpu_up((int)(op >> SCHEDOP_vcpushift));
- break;
- }
- case SCHEDOP_vcpu_pickle:
- {
- ret = do_vcpu_pickle((int)(op >> SCHEDOP_vcpushift), arg);
- break;
- }
default:
ret = -ENOSYS;
return 0;
}
-/** sched_id - fetch ID of current scheduler */
-int sched_id()
+/* sched_id - fetch ID of current scheduler */
+int sched_id(void)
{
return ops.sched_id;
}
--- /dev/null
+/******************************************************************************
+ * vcpu.h
+ *
+ * VCPU creation and hotplug.
+ *
+ * Copyright (c) 2005, Keir Fraser <keir@xensource.com>
+ */
+
+#ifndef __XEN_PUBLIC_VCPU_H__
+#define __XEN_PUBLIC_VCPU_H__
+
+/*
+ * Prototype for this hypercall is:
+ * int vcpu_op(int cmd, int vcpuid, void *extra_args)
+ * @cmd == VCPUOP_??? (VCPU operation).
+ * @vcpuid == VCPU to operate on.
+ * @extra_args == Operation-specific extra arguments (NULL if none).
+ */
+
+/*
+ * Create a new VCPU. This must be called before a VCPU can be referred to
+ * in any other hypercall (e.g., to bind event channels). The new VCPU
+ * will not run until it is brought up by VCPUOP_up.
+ *
+ * @extra_arg == pointer to vcpu_guest_context structure containing initial
+ * state for the new VCPU.
+ */
+#define VCPUOP_create 0
+
+/*
+ * Bring up a newly-created or previously brought-down VCPU. This makes the
+ * VCPU runnable.
+ */
+#define VCPUOP_up 1
+
+/*
+ * Bring down a VCPU (i.e., make it non-runnable).
+ * There are a few caveats that callers should observe:
+ * 1. This operation may return, and VCPU_is_up may return false, before the
+ * VCPU stops running (i.e., the command is asynchronous). It is a good
+ * idea to ensure that the VCPU has entered a non-critical loop before
+ * bringing it down. Alternatively, this operation is guaranteed
+ * synchronous if invoked by the VCPU itself.
+ * 2. After a VCPU is created, there is currently no way to drop all its
+ * references to domain memory. Even a VCPU that is down still holds
+ * memory references via its pagetable base pointer and GDT. It is good
+ * practise to move a VCPU onto an 'idle' or default page table, LDT and
+ * GDT before bringing it down.
+ */
+#define VCPUOP_down 2
+
+/* Returns 1 if the given VCPU is up. */
+#define VCPUOP_is_up 3
+
+#endif /* __XEN_PUBLIC_VCPU_H__ */
#define __HYPERVISOR_update_va_mapping_otherdomain 22
#define __HYPERVISOR_switch_vm86 23 /* x86/32 only */
#define __HYPERVISOR_switch_to_user 23 /* x86/64 only */
-#define __HYPERVISOR_boot_vcpu 24
+#define __HYPERVISOR_vcpu_op 24
#define __HYPERVISOR_set_segment_base 25 /* x86/64 only */
#define __HYPERVISOR_mmuext_op 26
#define __HYPERVISOR_acm_op 27
#define SCHEDOP_yield 0 /* Give up the CPU voluntarily. */
#define SCHEDOP_block 1 /* Block until an event is received. */
#define SCHEDOP_shutdown 2 /* Stop executing this domain. */
-#define SCHEDOP_vcpu_down 3 /* make target VCPU not-runnable. */
-#define SCHEDOP_vcpu_up 4 /* make target VCPU runnable. */
-#define SCHEDOP_vcpu_pickle 5 /* save a vcpu's context to memory. */
#define SCHEDOP_cmdmask 255 /* 8-bit command. */
#define SCHEDOP_reasonshift 8 /* 8-bit reason code. (SCHEDOP_shutdown) */
-#define SCHEDOP_vcpushift 8 /* 8-bit VCPU target. (SCHEDOP_up|down) */
/*
* Reason codes for SCHEDOP_shutdown. These may be interpreted by control